package com.ruoyi.fjtseac.util;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.UploadResult;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.transfer.*;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.SecurityUtils;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 腾讯对象存储COS的SDK接口使用
 * @author zzl
 * @date 2022/7/18 19:55
 */
@Service
public class COSUtil extends FileVerification implements FileUtil {

    @Value("${cos.host}")
    private String host;
    @Value("${cos.secretId}")
    private String secretId;
    @Value("${cos.secretKey}")
    private String secretKey;
    @Value("${cos.regionName}")
    private String regionName;
    @Value("${cos.bucketName}")
    private String bucketName;
    @Autowired
    RedisCache redisCache;

    // 创建 COSClient 实例，这个实例用来后续调用请求
    COSClient createCOSClient() {
        // 设置用户身份信息。
        // SECRETID 和 SECRETKEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理
        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);

        // ClientConfig 中包含了后续请求 COS 的客户端设置：
        ClientConfig clientConfig = new ClientConfig();

        // 设置 bucket 的地域
        // COS_REGION 请参照 https://cloud.tencent.com/document/product/436/6224
        clientConfig.setRegion(new Region(regionName));

        // 设置请求协议, http 或者 https
        // 5.6.53 及更低的版本，建议设置使用 https 协议
        // 5.6.54 及更高版本，默认使用了 https
        clientConfig.setHttpProtocol(HttpProtocol.https);

        // 以下的设置，是可选的：

        // 设置 socket 读取超时，默认 30s
        clientConfig.setSocketTimeout(30*1000);
        // 设置建立连接超时，默认 30s
        clientConfig.setConnectionTimeout(30*1000);

//        // 如果需要的话，设置 http 代理，ip 以及 port
//        clientConfig.setHttpProxyIp("httpProxyIp");
//        clientConfig.setHttpProxyPort(80);

        // 生成 cos 客户端。
        return new COSClient(cred, clientConfig);
    }

    // 创建 TransferManager 实例，这个实例用来后续调用高级接口
    TransferManager createTransferManager() {
        // 创建一个 COSClient 实例，这是访问 COS 服务的基础实例。
        // 详细代码参见本页: 简单操作 -> 创建 COSClient
        COSClient cosClient = createCOSClient();

        // 自定义线程池大小，建议在客户端与 COS 网络充足（例如使用腾讯云的 CVM，同地域上传 COS）的情况下，设置成16或32即可，可较充分的利用网络资源
        // 对于使用公网传输且网络带宽质量不高的情况，建议减小该值，避免因网速过慢，造成请求超时。
        ExecutorService threadPool = Executors.newFixedThreadPool(32);

        // 传入一个 threadpool, 若不传入线程池，默认 TransferManager 中会生成一个单线程的线程池。
        TransferManager transferManager = new TransferManager(cosClient, threadPool);

        // 设置高级接口的配置项
        // 分块上传阈值和分块大小分别为 5MB 和 1MB
        TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
        transferManagerConfiguration.setMultipartUploadThreshold(5*1024*1024);
        transferManagerConfiguration.setMinimumUploadPartSize(1*1024*1024);
        transferManager.setConfiguration(transferManagerConfiguration);

        return transferManager;
    }

    void shutdownTransferManager(TransferManager transferManager) {
        // 指定参数为 true, 则同时会关闭 transferManager 内部的 COSClient 实例。
        // 指定参数为 false, 则不会关闭 transferManager 内部的 COSClient 实例。
        transferManager.shutdownNow(true);
    }

    /**
     * 文件上传到COS
     * @param file 目标文件
     * @param type 类型
     * @return key 对象名（即bucket下的路径）
     */
    @Override
    public String upload(MultipartFile file, String type, String uuid) {
        //判断文件是否为空
        if (file == null || file.isEmpty()) {
            return FALSE;
        }
        //通过文件后缀过滤文件
        if (!extensionFilter(file, type)) {
            return FALSE;
        }

//        // 调用 COS 接口之前必须保证本进程存在一个 COSClient 实例，如果没有则创建
//        // 详细代码参见本页：简单操作 -> 创建 COSClient
//        COSClient cosClient = createCOSClient();

        // 使用高级接口必须先保证本进程存在一个 TransferManager 实例，如果没有则创建
// 详细代码参见本页：高级接口 -> 创建 TransferManager
        TransferManager transferManager = createTransferManager();

        // 对象键(Key)是对象在存储桶中的唯一标识，实际就是对象名。
        //阿里云的COS服务，若对象名中若存在空格则无法删除。
        String key = type + "/" + UUID.randomUUID() + "_" +file.getOriginalFilename();

//        // 这里创建一个 ByteArrayInputStream 来作为示例，实际中这里应该是您要上传的 InputStream 类型的流
//        long inputStreamLength = 1024 * 1024;
//        byte[] data = new byte[Math.toIntExact(inputStreamLength)];
//        InputStream inputStream = new ByteArrayInputStream(data);

        ObjectMetadata objectMetadata = new ObjectMetadata();

        PutObjectRequest putObjectRequest = null;
        try {
            // 上传的流如果能够获取准确的流长度，则推荐一定填写 content-length
            // 如果确实没办法获取到，则下面这行可以省略，但同时高级接口也没办法使用分块上传了
            InputStream inputStream = file.getInputStream();
            objectMetadata.setContentLength(inputStream.available());
            putObjectRequest = new PutObjectRequest(bucketName, key, inputStream, objectMetadata);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            Upload upload = transferManager.upload(putObjectRequest);
            showTransferProgress(upload, uuid);
//            PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
            UploadResult uploadResult = upload.waitForUploadResult();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

//        // 确认本进程不再使用 cosClient 实例之后，关闭之
//        cosClient.shutdown();
        shutdownTransferManager(transferManager);
        return host + "/" + key;
    }

    // 可以参考下面的例子，结合实际情况做调整

    /**
     * 获取视频上传进度
     * @param transfer
     */
    void showTransferProgress(Transfer transfer, String uuid) {
        // 这里的 Transfer 是异步上传结果 Upload 的父类
        System.out.println(transfer.getDescription());

        // transfer.isDone() 查询上传是否已经完成
        while (transfer.isDone() == false) {
            try {
                // 每 0.5 秒获取一次进度
                Thread.sleep(500);
            } catch (InterruptedException e) {
                return;
            }

            TransferProgress progress = transfer.getProgress();
            long sofar = progress.getBytesTransferred();
            long total = progress.getTotalBytesToTransfer();
            double pct = progress.getPercentTransferred();
            System.out.printf("upload progress: [%.02f / %.02f] = %.02f%%\n", sofar/1024.0/1024.0, total/1024.0/1024.0, pct);
            redisCache.setCacheObject(uuid, String.format("%d",(int)pct));
        }

        // 完成了 Completed，或者失败了 Failed
        //设置进度条在redis中的过期时间
        redisCache.expire(uuid, 480);
        System.out.println(transfer.getState());
    }

    @Override
    public boolean exists(String key) {
        // 调用 COS 接口之前必须保证本进程存在一个 COSClient 实例，如果没有则创建
        // 详细代码参见本页：简单操作 -> 创建 COSClient
        COSClient cosClient = createCOSClient();
        boolean objectExists = false;
        try {
            objectExists = cosClient.doesObjectExist(bucketName, key);
        } catch (CosClientException e) {
            e.printStackTrace();
        }

        // 确认本进程不再使用 cosClient 实例之后，关闭之
        cosClient.shutdown();
        return objectExists;
    }

    @Override
    public boolean delete(String... keys) {
        // 调用 COS 接口之前必须保证本进程存在一个 COSClient 实例，如果没有则创建
        // 详细代码参见本页：简单操作 -> 创建 COSClient
        COSClient cosClient = createCOSClient();
        boolean isSuccess = true;
        for (String key : keys) {
            try {
                cosClient.deleteObject(bucketName, key);
            } catch (CosClientException e) {
                isSuccess = false;
                e.printStackTrace();
            }
        }
        if (cosClient != null) {
            cosClient.shutdown();
        }
        return isSuccess;
    }

    @Override
    public void transcodeToH264(String inputUrl, String outputPath) {

    }

    @Override
    public Object getProgress(String uuid) {
        if (uuid == null) {
            return null;
        }
        return redisCache.getCacheObject(uuid);
    }
}
